///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once


#ifndef __AnimationTrack_H__
#define __AnimationTrack_H__

#include "OgrePrerequisites.h"
#include "OgreSimpleSpline.h"
#include "OgreRotationalSpline.h"
#include "OgreKeyFrame.h"
#include "OgreAnimable.h"
#include "OgrePose.h"

namespace Ogre 
{
	
    class _OgreExport TimeIndex
    {
    protected:
        
        Real mTimePos;
        
        uint mKeyIndex;

        
        static const uint INVALID_KEY_INDEX = (uint)-1;

    public:
        
        TimeIndex(Real timePos)
            : mTimePos(timePos)
            , mKeyIndex(INVALID_KEY_INDEX)
        {
        }

        
        TimeIndex(Real timePos, uint keyIndex)
            : mTimePos(timePos)
            , mKeyIndex(keyIndex)
        {
        }

        bool hasKeyIndex(void) const
        {
            return mKeyIndex != INVALID_KEY_INDEX;
        }

        Real getTimePos(void) const
        {
            return mTimePos;
        }

        uint getKeyIndex(void) const
        {
            return mKeyIndex;
        }
    };

    
	class _OgreExport AnimationTrack : public MemAlloc_Animation
    {
    public:

		
		class _OgreExport Listener
		{
		public:
			virtual ~Listener() {}

			
			virtual bool getInterpolatedKeyFrame(const AnimationTrack* t, const TimeIndex& timeIndex, KeyFrame* kf) = 0;
		};

        /// Constructor
        AnimationTrack(Animation* parent, unsigned short handle);

        virtual ~AnimationTrack();

		
		unsigned short getHandle(void) const { return mHandle; }

        
        virtual unsigned short getNumKeyFrames(void) const;

        
        virtual KeyFrame* getKeyFrame(unsigned short index) const;

        
        virtual Real getKeyFramesAtTime(const TimeIndex& timeIndex, KeyFrame** keyFrame1, KeyFrame** keyFrame2,
            unsigned short* firstKeyIndex = 0) const;

        
        virtual KeyFrame* createKeyFrame(Real timePos);

        
        virtual void removeKeyFrame(unsigned short index);

        
        virtual void removeAllKeyFrames(void);


        
        virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const = 0;

        
        virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) = 0;

        
		virtual void _keyFrameDataChanged(void) const {}

		
		virtual bool hasNonZeroKeyFrames(void) const { return true; }

		
		virtual void optimise(void) {}

        
        virtual void _collectKeyFrameTimes(vector<Real>::type& keyFrameTimes);

        
        virtual void _buildKeyFrameIndexMap(const vector<Real>::type& keyFrameTimes);
		
		
		virtual void _applyBaseKeyFrame(const KeyFrame* base) {}

		
		virtual void setListener(Listener* l) { mListener = l; }

		
		Animation *getParent() const { return mParent; }
    protected:
        typedef vector<KeyFrame*>::type KeyFrameList;
        KeyFrameList mKeyFrames;
        Animation* mParent;
		unsigned short mHandle;
		Listener* mListener;

        /// Map used to translate global keyframe time lower bound index to local lower bound index
        typedef vector<ushort>::type KeyFrameIndexMap;
        KeyFrameIndexMap mKeyFrameIndexMap;

		/// Create a keyframe implementation - must be overridden
		virtual KeyFrame* createKeyFrameImpl(Real time) = 0;

		/// Internal method for clone implementation
		virtual void populateClone(AnimationTrack* clone) const;
		


    };

	
	class _OgreExport NumericAnimationTrack : public AnimationTrack
	{
	public:
		/// Constructor
		NumericAnimationTrack(Animation* parent, unsigned short handle);
		/// Constructor, associates with an AnimableValue
		NumericAnimationTrack(Animation* parent, unsigned short handle, 
			AnimableValuePtr& target);

        
        virtual NumericKeyFrame* createNumericKeyFrame(Real timePos);

		/// @copydoc AnimationTrack::getInterpolatedKeyFrame
        virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const;

		/// @copydoc AnimationTrack::apply
		virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f);

        
		void applyToAnimable(const AnimableValuePtr& anim, const TimeIndex& timeIndex, 
			Real weight = 1.0, Real scale = 1.0f);

		
		virtual const AnimableValuePtr& getAssociatedAnimable(void) const;

		
		virtual void setAssociatedAnimable(const AnimableValuePtr& val);

		
		NumericKeyFrame* getNumericKeyFrame(unsigned short index) const;

		
		NumericAnimationTrack* _clone(Animation* newParent) const;


	protected:
		/// Target to animate
		AnimableValuePtr mTargetAnim;

		/// @copydoc AnimationTrack::createKeyFrameImpl
		KeyFrame* createKeyFrameImpl(Real time);


	};

	
	class _OgreExport NodeAnimationTrack : public AnimationTrack
	{
	public:
		/// Constructor
		NodeAnimationTrack(Animation* parent, unsigned short handle);
		/// Constructor, associates with a Node
		NodeAnimationTrack(Animation* parent, unsigned short handle, 
			Node* targetNode);
        /// Destructor
        virtual ~NodeAnimationTrack();
        
        virtual TransformKeyFrame* createNodeKeyFrame(Real timePos);
		
		virtual Node* getAssociatedNode(void) const;

		
		virtual void setAssociatedNode(Node* node);

		
		virtual void applyToNode(Node* node, const TimeIndex& timeIndex, Real weight = 1.0, 
			Real scale = 1.0f);

		
		virtual void setUseShortestRotationPath(bool useShortestPath);

		
		virtual bool getUseShortestRotationPath() const;

		/// @copydoc AnimationTrack::getInterpolatedKeyFrame
        virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const;

		/// @copydoc AnimationTrack::apply
		virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f);

		/// @copydoc AnimationTrack::_keyFrameDataChanged
		void _keyFrameDataChanged(void) const;

		
		virtual TransformKeyFrame* getNodeKeyFrame(unsigned short index) const;


		
		virtual bool hasNonZeroKeyFrames(void) const;

		
		virtual void optimise(void);

		
		NodeAnimationTrack* _clone(Animation* newParent) const;
		
		void _applyBaseKeyFrame(const KeyFrame* base);
		
	protected:
		/// Specialised keyframe creation
		KeyFrame* createKeyFrameImpl(Real time);
		// Flag indicating we need to rebuild the splines next time
		virtual void buildInterpolationSplines(void) const;

        // Struct for store splines, allocate on demand for better memory footprint
        struct Splines
        {
		    SimpleSpline positionSpline;
		    SimpleSpline scaleSpline;
		    RotationalSpline rotationSpline;
        };

		Node* mTargetNode;
		// Prebuilt splines, must be mutable since lazy-update in const method
		mutable Splines* mSplines;
		mutable bool mSplineBuildNeeded;
		/// Defines if rotation is done using shortest path
		mutable bool mUseShortestRotationPath ;
	};

	
	enum VertexAnimationType
	{
		/// No animation
		VAT_NONE = 0,
		/// Morph animation is made up of many interpolated snapshot keyframes
		VAT_MORPH = 1,
		/// Pose animation is made up of a single delta pose keyframe
		VAT_POSE = 2
	};

	
	class _OgreExport VertexAnimationTrack : public AnimationTrack
	{
	public:
		
		enum TargetMode
		{
			/// Interpolate vertex positions in software
			TM_SOFTWARE, 
			
			TM_HARDWARE
		};
		/// Constructor
		VertexAnimationTrack(Animation* parent, unsigned short handle, VertexAnimationType animType);
		/// Constructor, associates with target VertexData and temp buffer (for software)
		VertexAnimationTrack(Animation* parent, unsigned short handle, VertexAnimationType animType, 
			VertexData* targetData, TargetMode target = TM_SOFTWARE);

		
		VertexAnimationType getAnimationType(void) const { return mAnimationType; }
		
		
		bool getVertexAnimationIncludesNormals() const;

		
		virtual VertexMorphKeyFrame* createVertexMorphKeyFrame(Real timePos);

		
		virtual VertexPoseKeyFrame* createVertexPoseKeyFrame(Real timePos);

		
        virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const;

		/// @copydoc AnimationTrack::apply
		virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f);

		
		virtual void applyToVertexData(VertexData* data, 
			const TimeIndex& timeIndex, Real weight = 1.0, 
			const PoseList* poseList = 0);


		
		VertexMorphKeyFrame* getVertexMorphKeyFrame(unsigned short index) const;

		
		VertexPoseKeyFrame* getVertexPoseKeyFrame(unsigned short index) const;

		
		void setAssociatedVertexData(VertexData* data) { mTargetVertexData = data; }
		
		VertexData* getAssociatedVertexData(void) const { return mTargetVertexData; }

		/// Set the target mode
		void setTargetMode(TargetMode m) { mTargetMode = m; }
		/// Get the target mode
		TargetMode getTargetMode(void) const { return mTargetMode; }

		
		virtual bool hasNonZeroKeyFrames(void) const;

		
		virtual void optimise(void);

		
		VertexAnimationTrack* _clone(Animation* newParent) const;
		
		void _applyBaseKeyFrame(const KeyFrame* base);

	protected:
		/// Animation type
		VertexAnimationType mAnimationType;
		/// Target to animate
		VertexData* mTargetVertexData;
		/// Mode to apply
		TargetMode mTargetMode;

		/// @copydoc AnimationTrack::createKeyFrameImpl
		KeyFrame* createKeyFrameImpl(Real time);

		/// Utility method for applying pose animation
		void applyPoseToVertexData(const Pose* pose, VertexData* data, Real influence);


	};
	
	
}

#endif
